home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
pmode
/
pmode30b
/
pmode.doc
< prev
next >
Wrap
Text File
|
1994-05-31
|
88KB
|
2,049 lines
This is the documentation for PMODE v3.0 DPMI/VCPI/XMS/raw protected mode
interface kernel. Copyright (c) 1994, Tran (a.k.a. Thomas Pytel). PMODE is
publicly available and is not confidential or proprietary. I, Thomas Pytel,
reserve all rights to the source code. However, feel free to use or distribute
it in any manner you wish. All I ask, if you use this code in some production,
is credits for it.
------------------------------------------------------------------------------
Contents:
---------
0 - Introduction
0.0 - Disclaimer
0.1 - Description
1 - Overview
1.0 - Initialization and termination
1.1 - Segments, selectors, and descriptors
1.2 - Stacks and mode switching
1.3 - Interrupts
1.4 - Real mode callbacks
1.5 - PMODE specifics
2 - Functions
2.0 - Function 0000h - Allocate Descriptors
2.1 - Function 0001h - Free Descriptor
2.2 - Function 0003h - Get Selector Increment Value
2.3 - Function 0006h - Get Segment Base Address
2.4 - Function 0007h - Set Segment Base Address
2.5 - Function 0008h - Set Segment Limit
2.6 - Function 0009h - Set Descriptor Access Rights
2.7 - Function 000Ah - Create Alias Descriptor
2.8 - Function 000Bh - Get Descriptor
2.9 - Function 000Ch - Set Descriptor
2.10 - Function 000Eh - Get Multiple Descriptors
2.11 - Function 000Fh - Set Multiple Descriptors
2.12 - Function 0200h - Get Real Mode Interrupt Vector
2.13 - Function 0201h - Set Real Mode Interrupt Vector
2.14 - Function 0204h - Get Protected Mode Interrupt Vector
2.15 - Function 0205h - Set Protected Mode Interrupt Vector
2.16 - Function 0300h - Simulate Real Mode Interrupt
2.17 - Function 0301h - Call Real Mode Procedure With Far Return Frame
2.18 - Function 0302h - Call Real Mode Procedure With IRET Frame
2.19 - Function 0303h - Allocate Real Mode Callback Address
2.20 - Function 0304h - Free Real Mode Callback Address
2.21 - Function 0305h - Get State Save/Restore Addresses
2.22 - Function 0306h - Get Raw Mode Switch Addresses
2.23 - Function 0400h - Get Version
2.24 - Function 0500h - Get Free Memory Information
2.25 - Function 0501h - Allocate Memory Block
2.26 - Function 0502h - Free Memory Block
2.27 - Function 0503h - Resize Memory Block
2.28 - Function 050Ah - Get Memory Block Size and Base
2.29 - Function 0900h - Get and Disable Virtual Interrupt State
2.30 - Function 0901h - Get and Enable Virtual Interrupt State
2.31 - Function 0902h - Get Virtual Interrupt State
3 - Miscellaneous
3.0 - Glossary
3.1 - Differences among modes
3.2 - Notes
3.3 - Final word
------------------------------------------------------------------------------
0 - Introduction:
-----------------
This document will not attempt to explain the workings of protected mode. If
you are new to protected mode coding, I suggest you get yourself a good book.
I also suggest you get your hands on some good DPMI documentation as a
reference and background for understanding PMODE. This document is only
intended to explain the workings of PMODE for the purpose of using it directly
or for writing a shell or high level language interface.
0.0 Disclaimer:
---------------
Legal:
I exclude any and all implied warranties, including warranties of
merchantability and fitness for a particular purpose. I make no warranty or
representation, either express or implied, with respect to this source code,
its quality, performance, merchantability, or fitness for a particular
purpose. I shall have no liability for special, incidental or consequential
damages arising out of or resulting from the use or modification of this
source code.
English:
If you fuck up, its your own problem.
0.1 Description:
----------------
PMODE v3.0 is basically a DOS extender. It allows DOS programs to run in
full protected mode. PMODE will take care of all the system details, the
descriptor tables, extended memory management, interrupts, etc... It does not
matter what kind of system is already in place. DPMI, VCPI, XMS, and a clean
system will all be handled appropriately. If DPMI is in place, PMODE will do
basically nothing, and your code will be talking directly to the DPMI host.
But if it is not, PMODE will provide a subset of DPMI functionality to your
code. It is slightly annoying that most of the DPMI interface uses pairs of
16bit registers for 32bit values. This goes back to the 80286 support of DPMI.
Code using PMODE can set up its own descriptors. It can run across real
mode, 16bit protected mode, and 32bit protected mode. Full extended memory
management is provided. Blocks of extended memory can be allocated, resized,
and freed. PMODE does not manage low memory, Your code is responsible for the
memory below the 1M boundary. The PMODE kernel is well suited for a shell to
extend its functionality. A shell to provide extended or simplified services
to assembly or high level code.
I wrote PMODE with attention to speed. Wherever I could control it, I tried
to ensure that code using PMODE would run as fast as possible. I also tried to
make sure PMODE is very stable. Under DPMI, your code is a slave to the DPMI
host's whims. Almost definately running at CPL 3, which makes it quite slow.
There is nothing I can do about that. Well, locating and messing with the DPMI
host's system tables might not be too hard, but too unpredictable. Running in
protected mode at CPL 3 is better than real mode CPL 3, because you can avoid
loading segment registers very often (which is slow) by setting up flat
memory. However, if DPMI is not present, you can be sure your code will be
running as fast as it possibly can. Under a VCPI, XMS, or raw system, PMODE
will run your code at CPL 0. There are no I/O permission bitmap checks on port
accesses or task switching. Under an XMS or raw system, real mode calls are
executed in actual real mode rather than the slower V86 mode, which is
actually protected mode at CPL 3. Under VCPI, real mode calls are executed in
V86 mode, which is what the VCPI server normally runs DOS in. Also, under
VCPI, paging is enabled. It is a minor speed degredation factor, but one that
is avoided under XMS or raw.
I've been coding 386 protected mode systems for a couple of years now. I
tried to make PMODE as clean as possible. But it was coded from scratch.
Though I tested it extensively, there is unfortunately always the possibility
of a bug. I am pretty confident it is clean though, because I went over EVERY
line of code when finally done with it. I also plugged it into some old
programs using a previous version of PMODE, which did well to help me find a
few bugs.
------------------------------------------------------------------------------
1 - Overview:
-------------
I adopted the DPMI interface for PMODE to make code that works with PMODE
very portable to other extenders. Only a subset of DPMI is supported, both to
keep the size of the kernel down, and because I am lazy (the latter being the
more important factor). The interface to PMODE is INT 31h in protected mode.
Functions are available for descriptors, interrupt vectors, extended memory,
real mode callbacks, and calling real mode interrupts and procedures. PMODE
works as a subset of DPMI 1.0 rather than DPMI 0.9. This means that error
codes are returned from unsuccessful function calls, and some functions are
available that are not available under DPMI 0.9. Note though, that if the
system is already under DPMI 0.9, PMODE code is not active, so error codes
will not be returned and only DPMI 0.9 functions will be available.
1.0 - Initialization and termination:
-------------------------------------
There are only two functions immediately callable in PMODE. They are
_pm_info and _pm_init. _pm_info returns some information about the current
type of system and the low memory requirements for protected mode. No matter
what the system, DPMI, VCPI, XMS, or raw, a low memory buffer is required for
protected mode operation. The size of this buffer is returned from _pm_info.
Your code is responsible for providing that buffer to _pm_init.
_pm_init switches the system into protected mode. If DPMI is in place, all
that is done is a switch into 32bit protected mode. If the DPMI host does not
support 32bit protected mode, _pm_init will return an error. 32bit protected
mode does not necessarily mean your code has to run in a 32bit segment with
default 32bit instructions. It just means that it is possible. Since DPMI is
defined for 80286 computers, and you might even find DPMI that is capable of
32bit protected mode refusing this request on a 386 system.
_pm_init will return with the carry flag set and an error code in AX if an
error ocurred while trying to switch into protected mode. If the switch to
protected mode was successful, the carry flag will be clear, and the system
will be in protected mode. The CS segment register will have been converted
to a protected mode selector corresponding to a descriptor mapping the same
memory as it did in real mode. Likewise, the DS and SS segment registers will
be converted to selectors. If DS and SS were equal before the call to
_pm_init, the same selector may be returned in both. ES will contain a
selector for your program's PSP. The environment segment at PSP:2ch will also
be converted to a selector if it was a non-zero value before the call to
_pm_init. FS and GS will be returned as 0 (NULL selector). Your code will now
be running in a 16bit protected mode code segment, with full access to the
protected mode INT 31h functions. If the system is DPMI, this is the last your
code will have talked to PMODE, and from now on, will be using the DPMI host
directly.
Both functions are FAR, and the full calling format is as follows:
) _pm_info - Get protected mode info:
In:
None
Out:
AX - return code:
0000h - successful
0001h - no 80386+ detected
0002h - system already in protected mode and no VCPI or DPMI found
0003h - DPMI - host is not 32bit
CF - set on error, if no error:
BX - number of paragraphs needed for protected mode data (may be 0)
CL - processor type:
03h - 80386
04h - 80486
05h - 80586
06h-FFh - reserved for future use
CH - protected mode type:
00h - raw
01h - XMS
02h - VCPI
03h - DPMI
) _pm_init - Initialize protected mode:
In:
ES - real mode segment for protected mode data (ignored if not needed)
Out:
AX - return code:
0000h - successful
0001h - no 80386+ detected
0002h - system already in protected mode and no VCPI or DPMI found
0003h - DPMI - host is not 32bit
0004h - could not enable A20 gate
0005h - DPMI - could not enter 32bit protected mode
0006h - DPMI - could not allocate needed selectors
CF - set on error, if no error:
ESP - high word clear
CS - 16bit selector for real mode CS with limit of 64k
SS - 32bit selector for real mode SS with limit of 64k
DS - 32bit selector for real mode DS with limit of 64k
ES - 32bit selector for PSP with limit of 100h
FS - 0 (NULL selector)
GS - 0 (NULL selector)
The CS, DS, and SS selectors returned from _pm_init can be modified or
freed by your code. The PSP selector and the converted environment selector in
the PSP may not be. There is a special case when the CS selector returned from
_pm_init may not be freed or modified. That is when the FAR CALL to _pm_init
came from the PMODE_TEXT segment. In this case, PMODE will return its own code
selector which maps PMODE_TEXT. This is just a minor optimization provided for
any shell that has its code in PMODE_TEXT to save a descriptor.
To terminate under PMODE, your code must issue an INT 21h function 4ch in
protected mode. Just like in real mode, AL is the return code. Your code
should only terminate from the main stream of execution. That is, do not try
to quit from a protected mode IRQ handler or a real mode callback. Before
termination, your code has the following responsibilities:
) Restore any real mode interrupt vectors which were hooked.
) Free any extended memory blocks that were allocated.
1.1 - Segments, selectors, and descriptors:
-------------------------------------------
As you know (I hope), in protected mode, selectors are used in place of
actual segment values in segment registers. Selectors are basically indexes
into system tables which contain all the information about segments in
descriptors. You can think of selectors as handles to segments. They are
independent of the actual location and size of the segment in memory.
Under PMODE, your code can allocate its own selectors and descriptors. You
can set up code segments, data segments, and your own stack segments. These
segments can be either 16bit, 32bit, or a mix. The best use of the flexibility
of protected mode is to set up very large segments, in effect, eliminating the
need for segmentation. You can set up a code descriptor, and set its size to
4G. Then a data descriptor of that same size and with the same base address.
When all memory is addressable from a single segment, there is little need for
other segments. But they are available, possibly for code modules to be loaded
from disk into seperate segments.
After allocating a descriptor, you code can set it up in one shot, with
Set Descriptor function (000ch). Or you can set the base address, limit, and
access rights/type seperately. You can also allocate a descriptor and have it
automatically set to the same base address and size as another descriptor
using the Create Alias Descriptor function (000ah). You code can also read a
whole descriptor or base address of a descriptor. There is no Get Segment
Limit function because the LSL instruction performs that function. The LAR
instruction returns the access rights/type of a descriptor.
Technically, it is possible for DPMI to deny requests to set up very large
segments for protection reasons. But no DPMI host that I know of does this.
They all do protection at the paging level. DPMI 1.0 specifies a function
that returns an absolute ceiling for large segments. Allowing for flat memory,
but falling short of the full 4G linear address range. I would not worry about
this. If DPMI 1.0 hosts started to deny requests to set segment sizes to 4G,
many protected mode extended programs would cease to function.
When setting up a data descriptor which will be used as a stack segment, be
aware that the B bit will determine whether PUSHes and POPs on that stack use
SP or ESP as the top of stack pointer. However, even if you use a stack with
the B bit clear (using SP), ESP should still be the top of stack pointer.
Which means that when using a 16bit stack segment, the high word of ESP MUST
be clear.
1.2 - Stacks and mode switching:
--------------------------------
Within your main stream of execution, your code can set up its own stack.
But there are times when a stack is provided to your code and your code should
stay on that stack. At those times, your code may switch stacks during
processing, but should return on the same stack it was called. This is during
servicing of hardware interrupts or real mode callbacks in protected mode.
Switching between protected mode and real mode can be accomplished in one of
many ways. In protected mode, the default IRQ handlers switch to real mode to
execute the real mode handler for the specific IRQ that was called. A software
INT instruction in protected mode is also, by default, sent to on to real mode
for processing. There are three specific functions which allow you to call
real mode interrupts and procedures in a much more structured manner. There
are real mode callbacks. These are basically addresses in real mode which,
when called in real mode, transfer control to protected mode routines defined
by your code. And finally, there is raw mode switching. Your code can obtain
the addresses of a real mode routine which will switch the system into
protected mode, and a protected mode routine which will switch the system into
real mode. This is the lowest level, and quickest, method of switching modes.
IRQ and INT redirection is discussed later, as are real mode callbacks.
The INT 31h functions 0300h, 0301h, and 0302h allow your code to call real
mode interrupts of FAR routines. Since protected mode selectors are not valid
in real mode, your must pass the values to load into the segment registers in
real mode, for the interrupt or routine, in a memory structure. This structure
also contains the general registers as you want to pass them to real mode.
Using these INT 31h functions, you can specify a portion of data from the
protected mode stack to be put on the real mode stack for the interrupt or
procedure call. You also do not have to provide a real mode stack. If you set
the SS and SP fields in the register structure to 0, PMODE will provide a real
mode stack for the real mode call. If you prefer, however, you can provide the
stack yourself. Upon return from the real mode call, the register structure
will contain the values that were passed back in the registers from the real
mode interrupt handler or procedure. The CS, IP, SS, and SP fields will remain
unmodified though.
Using the raw mode switching routines is the fastest way to switch between
modes. However, if these functions are to be used, special measures must be
taken to preserve the state of the system. If using raw mode switching, you
must use the state save/restore fuctions whose addresses you can obtain with
INT 31h function 0305h. The state is saved in a buffer you provide. The stack
is a good place for this buffer. Some example code is in order:
buffersize dd ? ; size of state buffer
pmstate df ? ; selector:offset of state routine
pmtorm df ? ; selector:offset of switch routine
rmstate dd ? ; segment:offset of state routine
rmtopm dd ? ; segment:offset of switch routine
; this code gets and stores the addresses of the various routines
mov ax,305h ; get addresses of state save/restore
int 31h ; routines
movzx eax,ax ; zero high word of EAX
mov buffersize,eax ; size of state buffer
mov word ptr rmstate[0],cx ; offset of real mode state routine
mov word ptr rmstate[2],bx ; segment of real mode state routine
mov dword ptr pmstate[0],edi ; offset of protected mode routine
mov word ptr pmstate[4],si ; selector of protected mode routine
mov ax,306h ; get addresses of mode switch
int 31h ; routines
mov word ptr rmtopm[0],cx ; offset of real mode switch routine
mov word ptr rmtopm[2],bx ; segment of real mode switch routine
mov dword ptr pmtorm[0],edi ; offset of protected mode routine
mov word ptr pmtorm[4],si ; selector of protected mode routine
; this code saves the state and jumps to real mode
sub esp,buffersize ; allocate buffer space on stack
mov edi,esp ; set ES:EDI = SS:ESP, buffer address
mov ax,ss
mov es,ax
xor al,al ; set AL = 0, save state
call pmstate ; save state
mov ax,real_mode_DS_value ; set values for real mode registers
mov cx,real_mode_ES_value
mov dx,real_mode_SS_value
mov bx,real_mode_SP_value
mov si,real_mode_CS_value
mov di,real_mode_IP_value
jmp pmtorm ; switch to real mode
; this would restore the state after a return from real mode
mov edi,esp ; set ES:EDI = SS:ESP, buffer address
mov ax,ss
mov es,ax
mov al,1 ; set AL = 1, restore state
call pmstate ; restore state
add esp,buffersize ; discard stack buffer space
Real mode code to save/restore the state and call protected mode would be
similar, except that EBX would be used to pass a value for ESP, and EDI would
be used to pass EIP rather than IP in switching modes. Also, ES:DI would be
used as the state buffer rather than ES:EDI.
1.3 - Interrupts:
-----------------
When protected mode is first entered, all interrupts except those providing
DPMI functionality are directed to a handler which will pass them on to real
mode. That is, a software INT instruction executed by your code in protected
mode will cause the CPU to be switched to real mode and the interrupt will be
re-issued in real mode. After the interrupt handler returns, the system will
be switched back to protected mode. All general registers (EAX, EBX, ECX, EDX,
ESI, EDI, and EBP) in protected mode are passed on to the real mode handler,
and the general registers and flags are passed back from real mode. The
segment registers are not passed on to real mode since segment registers have
different meanings in protected mode and real mode. This means that you can
call simple interrupt routines which do not require values in segment
registers, such as the keyboard BIOS function 0 to read a character from the
keyboard, by just setting AH to 0 and issuing an INT 16h in protected mode. If
you need to pass segment registers to a real mode interrupt handler, you must
use the INT 31h function 0300h.
IRQs are likewise passed on to real mode. PMODE will not, for the sake of a
little speed increase, pass any registers to or from a real mode IRQ handler.
A real DPMI host probably will pass the general registers just as it would for
a software INT instruction. You may hook a protected mode interrupt vector for
any interrupt, 0-0ffh, and process the interrupt entirely in protected mode if
you wish. Or you can do some processing in protected mode, then chain to the
real mode handler by passing control to the previous handler for the interrupt
your code hooked.
PMODE will provide a real mode stack for both the software INT redirection
and the hardware IRQ redirection to real mode. If a real mode stack is
unavailable because of too many nested calls to real mode, the PC speaker will
be turned on and the machine will be hung. I prefer this to clunky exception
code messing up my nice and pretty extender.
In protected mode, when interrupt handlers are called, the interrupt flag is
not disabled like it is in real mode. This is done only for IRQs and
interrupts 0-7. Handlers for all other interrupts in protected mode must not
assume the interrupt flag has been cleared for them. If they need interrupts
disabled, they must do it themselves.
Under DPMI, the interrupt flag may need to be virtualized. I will spare you
a long explanation because any good DPMI text will give that to you. But I
will give you some rules:
) You must not assume anything about instructions that would normally affect
the interrupt flag. Instructions like POPF and IRETD may have no effect on
the current status of the interrupt flag. Also, PUSHF or an INT may not
store the interrupt flag correctly, so do not trust them to get information
on the interrupt flag.
) The only things you can be sure will affect the interrupt flag are CLI, STI,
and INT 31h functions 0900h and 0901h.
) If you need to learn the current status of the interrupt flag, you must use
one of the INT 31h functions, 0900h to clear and get the status of the
interrupt flag, 0901h to set and get the status of the flag, or 0902h which
simply returns the current value of the interrupt flag.
) Since IRETD may not affect the interrupt flag, you should re-enable
interrupts in a protected mode IRQ handler. This is because the interrupt
flag will have been cleared upon entry to the handler.
When writing interrupt handlers, you must terminate them with an IRETD, not
a simple IRET. For hardware IRQ handlers, you may want to make sure that any
code and data that may be touched by the IRQ handler resides in low memory,
below 1M. This memory is locked under DPMI, and will prevent having to swap
from disk at interrupt time under DPMI hosts which support virtual memory. One
other thing you should remember about IRQ handlers. IRQ 2 is really IRQ 9.
Devices which say they use IRQ 2 are actually using IRQ 9. In real mode, the
BIOS handler for IRQ 9 redirects it to the handler for IRQ 2. There is no such
redirection done in protected mode. So if you wish to write a handler for
IRQ 2 in protected mode, you must put it on IRQ 9. And remember to send the
EOI to the second interrupt controller. The BIOS IRQ 9 handler does that, but
you don't have the BIOS anymore.
1.4 - Real mode callbacks:
--------------------------
Real mode callbacks allow code running in real mode to call protected mode
procedures in a transparent manner. The real mode code thinks it is passing
control to another real mode procedure. This is, in reality, a real mode
callback. Which is basically a small routine which stores the values of the
real mode registers in a structure, then switches to protected mode and passes
control to a protected mode routine you specify.
Callbacks are allocated and freed just like descriptors or memory. When
allocating a callback, your code specifies the address of the protected mode
routine that is to gain control when the real mode callback is called. You
must also specify the selector:offset of a memory structure that is to recieve
the contents of the registers in real mode. The format of this structure is
the same as for INT 31h functions 0300h, 0301h, and 0302h. When the protected
mode routine for a real mode callback gets control, interrupts will be
disabled, and the following registers are defined:
DS:ESI - selector:offset corresponding to real mode SS:SP
ES:EDI - selector:offset of real mode register data structure
SS:ESP - protecterd mode stack provided by PMODE or the DPMI host
The real mode register data structure has the following format:
Offset Length Contents
00h 4 EDI
04h 4 ESI
08h 4 EBP
0ch 4 reserved, ignored
10h 4 EBX
14h 4 EDX
18h 4 ECX
1ch 4 EAX
20h 2 CPU status flags
22h 2 ES
24h 2 DS
26h 2 FS
28h 2 GS
2ah 2 IP, undefined
2ch 2 CS, undefined
2eh 2 SP
30h 2 SS
All fields except the CS and IP are filled in with the contents of the real
mode registers when the real mode callback got control. The protected mode
callback procedure can extract its parameters from the register data structure
and/or the real mode stack. Remember that the segment registers contain real
mode segment addresses, not protected mode selectors.
The protected mode callback procedure exits with an IRETD with the address
of the real mode register data structure in ES:EDI. Information can be passed
back to real mode by modifying the contents of the register data structure and
the real mode stack. The protected mode callback routine is responsible for
setting the correct address for the resumption of execution in real mode in
the CS:IP fields of the register data structure. It is also responsible for
updating the SS:SP fields in the register data structure to remove the address
of the calling real mode routine from the real mode stack.
The real mode register data structure and the DS selector used to map the
real mode SS segment are static. This is not a problem if you leave interrupts
disabled throughout the protected mode callback routine. But if you intend to
re-enable interrupts, you must make sure you do not use the DS selector
anymore. If you need to access the real mode stack after enabling interrupts,
you must create an alias descriptor for the DS selector passed to your
callback procedure. Or you can take other measures, but just remember that the
original DS selector is no longer safe after enabling interrupts in a
protected mode callback routine.
Since the real mode register data structure is also static, you should take
special care if you wish to enable interrupts in a callback. Since the data
structure is needed for the exit from the callback, you can not simply
discard it. You should make a copy of the data structure. You can then pass
back the copy when you exit the callback procedure. The ES:EDI upon exit is
not required to be the same as on entry.
1.5 - PMODE specifics:
----------------------
PMODE makes several variables public. These control certain things when
running under VCPI, XMS, or a raw system. Under DPMI, these variables do
absolutely nothing. The size of the low memory protected mode buffer required
for _pm_init is directly affected by these variables. Thus, they must be set
to the same values for the call to _pm_info and _pm_init. You should only
modify these variables before switching into protected mode.
) _pm_pagetables - This specifies the number of page tables you want to have
under VCPI. Each page table requires 4k and maps 4M of linear memory. Page
tables only define linear address space, not actual physical memory. The
amount of extended memory that will be available to your code will be the
lesser of linear and physical memory in the system. Setting a higher number
of page tables will not give you any more physical memory than is available
in the system, but it will give you more linear address space where physical
memory can be mapped. This helps reduce fragmentation of memory when
allocating extended memory blocks under VCPI. Never set this variable to 0,
since the first page table maps the low megabyte of real mode memory.
) _pm_selectors - This is the total number of descriptors you want PMODE to
make available to your code for allocation. The range for this variable is
0 to about 8150. The actual max number of descriptors that can exist in the
global descriptor table, which is where descriptors under PMODE reside, is
8191. But some of these descriptors are used by PMODE. Also, DPMI may not
be able to provide quite as many as 8000 descriptors. Each descriptor takes
up 8 bytes of space in the low memory protected mode buffer.
) _pm_callbacks - This is the number of real mode callbacks you want PMODE to
provide for allocation. Each callback takes up 19 bytes of space. You may
set this variable to 0.
) _pm_rmstacklen - This is the size of the real mode stack, in paragraphs,
that is provided for IRQ and INT redirection to real mode. Also for INT 31h
functions 0300h, 0301h, and 0302h when the SS:SP field in the register
structure is zero.
) _pm_rmstacks - This is the number of real mode stacks you want present in
case of nested calls to real mode. There must be at least one.
) _pm_pmstacklen - This is the size of the protected mode stack, in
paragraphs, to provide for protected mode procedures which handle real mode
callbacks.
) _pm_pmstacks - This is the number of protected mode stacks you want present
in case of nested callbacks. If your code is not going to be using real mode
callbacks, you may set this variable, along with _pm_pmstacklen and
_pm_callbacks, to zero.
------------------------------------------------------------------------------
2 - Functions:
--------------
PMODE duplicates a subset of DPMI protected mode functions. These functions
are available ONLY in protected through INT 31h. They provide descriptor
services, extended memory services, interrupt services, translation services,
and some other misc things. A function is called by setting AX to the function
code, setting any other registers for the function, and executing an INT 31h.
Upon return, the carry flag will be clear if the function was successful. If
the carry flag is set, the function failed. In this case, an error code will
be placed in AX. However, DPMI 0.9 will not return error codes, just the carry
flag set on errors. All other registers are preserved unless otherwise stated.
2.0 - Function 0000h - Allocate Descriptors:
--------------------------------------------
Allocates one or more descriptors in the client's descriptor table. The
descriptor(s) allocated must be initialized by the application with other
function calls.
In:
AX = 0000h
CX = number of descriptors to allocate
Out:
if successful:
AX = base selector
if failed:
AX = error code:
8011h - descriptor unavailable
8021h - invalid value (CX = 0) (VCPI/XMS/raw only)
Notes:
) If more that one descriptor was requested, the function returns a base
selector referencing the first of a contiguous array of descriptors. The
selector values for subsequent descriptors in the array can be calculated
by adding the value returned by INT 31h function 0003h.
) The allocated descriptor(s) will be set to expand-up writeable data, with
the present bit set and a base and limit of zero. The privilege level of the
descriptor(s) will match the client's code segment privilege level,
2.1 - Function 0001h - Free Descriptor:
---------------------------------------
Frees a descriptor.
In:
AX = 0001h
BX = selector for the descriptor to free
Out:
if failed:
AX = error code:
8022h - invalid selector
Notes:
) Each descriptor allocated with INT 31h function 0000h must be freed
individually with the function. Even if it was previously allocated as part
of a contiguous array of descriptors.
) Under DPMI 1.0/VCPI/XMS/raw, any segment registers which contain the
selector being freed are zeroed by this function.
2.2 - Function 0003h - Get Selector Increment Value:
----------------------------------------------------
The Allocate Descriptors function (0000h) can allocate an array of contiguous
descriptors, but only return a selector for the first descriptor. The value
returned by this function can be used to calculate the selectors for
subsequent descriptors in the array.
In:
AX = 0003h
Out:
always successful:
AX = selector increment value
Notes:
) The increment value is always a power of two.
2.3 - Function 0006h - Get Segment Base Address:
------------------------------------------------
Returns the 32bit linear base address from the descriptor table for the
specified segment.
In:
AX = 0006h
BX = selector
Out:
if successful:
CX:DX = 32bit linear base address of segment
if failed:
AX = error code:
8022h - invalid selector
Notes:
) Client programs must use the LSL instruction to query the limit for a
descriptor.
2.4 - Function 0007h - Set Segment Base Address:
------------------------------------------------
Sets the 32bit linear base address field in the descriptor for the specified
segment.
In:
AX = 0007h
BX = selector
CX:DX = 32bit linear base address of segment
Out:
if failed:
AX = error code:
8022h - invalid selector
8025h - invalid linear address (changing the base would cause the
descriptor to reference a linear address range outside that
allowed for DPMI clients) (DPMI 1.0 only)
Notes:
) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the
selector specified in register BX will be reloaded. DPMI 0.9 may do this,
but it is not guaranteed.
) I hope you have enough sense not to try to modify your current CS or SS
descriptor.
2.5 - Function 0008h - Set Segment Limit:
-----------------------------------------
Sets the limit field in the descriptor for the specified segment.
In:
AX = 0008h
BX = selector
CX:DX = 32bit segment limit
Out:
if failed:
AX = error code:
8021h - invalid value (the limit is > 1M, but the low 12 bits are
not set)
8022h - invalid selector
8025h - invalid linear address (changing the base would cause the
descriptor to reference a linear address range outside that
allowed for DPMI clients) (DPMI 1.0 only)
Notes:
) The value supplied to the function in CX:DX is the byte length of the
segment-1.
) Segment limits greater than or equal to 1M must be page aligned. That is,
they must have the low 12 bits set.
) This function has an implicit effect on the "G" bit in the segment's
descriptor.
) Client programs must use the LSL instruction to query the limit for a
descriptor.
) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the
selector specified in register BX will be reloaded. DPMI 0.9 may do this,
but it is not guaranteed.
) I hope you have enough sense not to try to modify your current CS or SS
descriptor.
2.6 - Function 0009h - Set Descriptor Access Rights:
----------------------------------------------------
Modifies the access rights field in the descriptor for the specified segment.
In:
AX = 0009h
BX = selector
CX = access rights/type word
Out:
if failed:
AX = error code:
8021h - invalid value (access rights/type word invalid)
8022h - invalid selector
8025h - invalid linear address (changing the base would cause the
descriptor to reference a linear address range outside that
allowed for DPMI clients) (DPMI 1.0 only)
Notes:
) The access rights/type word passed to the function in CX has the following
format:
Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| G |B/D| 0 | ? | ? | 1 | DPL | 1 |C/D|E/C|W/R| A |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
G - 0=byte granular, 1=page granular
B/D - 0=default 16bit, 1=default 32bit
DPL - must be equal to caller's CPL
C/D - 0=data, 1=code
E/C - data: 0=expand-up, 1=expand-down
code: must be 0 (non-conforming)
W/R - data: 0=read, 1=read/write
code: must be 1 (readable)
A - 0=not accessed, 1=accessed
0 - must be 0
1 - must be 1
? - ignored
) Client programs should use the LAR instruction to examine the access rights
of a descriptor.
) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the
selector specified in register BX will be reloaded. DPMI 0.9 may do this,
but it is not guaranteed.
) I hope you have enough sense not to try to modify your current CS or SS
descriptor.
2.7 - Function 000Ah - Create Alias Descriptor:
-----------------------------------------------
Creates a new data descriptor that has the same base and limit as the
specified descriptor.
In:
AX = 000ah
BX = selector
Out:
if successful:
AX = data selector (alias)
if failed:
AX = error code:
8011h - descriptor unavailable
8022h - invalid selector
Notes:
) The selector supplied to the function may be either a data descriptor or
a code descriptor. The alias descriptor created is always an expand-up
writeable data segment.
) The descriptor alias returned by this function will not track changes to the
original descriptor.
2.8 - Function 000Bh - Get Descriptor:
--------------------------------------
Copies the descriptor table entry for the specified selector into an 8 byte
buffer.
In:
AX = 000bh
BX = selector
ES:EDI = selector:offset of 8 byte buffer
Out:
if successful:
buffer pointed to by ES:EDI contains descriptor
if failed:
AX = error code:
8022h - invalid selector
2.9 - Function 000Ch - Set Descriptor:
--------------------------------------
Copies the contents of an 8 byte buffer into the descriptor for the specified
selector.
In:
AX = 000ch
BX = selector
ES:EDI = selector:offset of 8 byte buffer containing descriptor
Out:
if failed:
AX = error code:
8021h - invalid value (access rights/type word invalid)
8022h - invalid selector
8025h - invalid linear address (changing the base would cause the
descriptor to reference a linear address range outside that
allowed for DPMI clients) (DPMI 1.0 only)
) The descriptors access rights/type word at offset 5 within the descriptor
follows the same format and restrictions as the access rights/type parameter
CX to the Set Descriptor Access Rights function (0009h).
) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the
selector specified in register BX will be reloaded. DPMI 0.9 may do this,
but it is not guaranteed.
) I hope you have enough sense not to try to modify your current CS or SS
descriptor or the descriptor of the buffer.
2.10 - Function 000Eh - Get Multiple Descriptors:
-------------------------------------------------
Copies one or more descriptor table entries into a buffer.
In:
AX = 000eh
CX = number of descriptors to copy
ES:EDI = selector:offset of a buffer in the following format:
Offset Length Contents
00h 2 Selector #1 (set by client)
02h 8 Descriptor #1 (returned by host)
0ah 2 Selector #2 (set by client)
0ch 8 Descriptor #2 (returned by host)
... ... ...
Out:
if successful:
buffer contains copies of the descriptors for the specified selectors
if failed:
AX = error code:
8022h - invalid selector
CX = number of descriptors successfully copied
Notes:
) If an error occurs because of an invalid selector or descriptor, the
function returns the number of descriptors which were successfully copied
in CX. All of the descriptors which were copied prior to the one that failed
are valid.
) This function is not present under DPMI 0.9.
2.11 - Function 000Fh - Set Multiple Descriptors:
-------------------------------------------------
Copies one or more descriptors from a client buffer into the descriptor table.
In:
AX = 000fh
CX = number of descriptors to copy
ES:EDI = selector:offset of a buffer in the following format:
Offset Length Contents
00h 2 Selector #1
02h 8 Descriptor #1
0ah 2 Selector #2
0ch 8 Descriptor #2
... ... ...
Out:
if failed:
AX = error code:
8021h - invalid value (access rights/type word invalid)
8022h - invalid selector
8025h - invalid linear address (changing the base would cause the
descriptor to reference a linear address range outside that
allowed for DPMI clients) (DPMI 1.0 only)
CX = number of descriptors successfully copied
Notes:
) If an error occurs because of an invalid selector or descriptor, the
function returns the number of descriptors which were successfully copied in
CX. All of the descriptors which were copied prior to the one that failed
are valid.
) The descriptors access rights/type word at offset 5 within the descriptor
follows the same format and restrictions as the access rights/type parameter
CX to the Set Descriptor Access Rights (0009h) function.
) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains a selector
specified in the data structure will be reloaded. DPMI 0.9 may do this,
but it is not guaranteed.
) I hope you have enough sense not to try to modify your current CS or SS
descriptor or the descriptor of the buffer.
) This function is not present under DPMI 0.9.
2.12 - Function 0200h - Get Real Mode Interrupt Vector:
-------------------------------------------------------
Returns the real mode segment:offset for the specified interrupt vector.
In:
AX = 0200h
BL = interrupt number
Out:
always successful:
CX:DX = segment:offset of real mode interrupt handler
Notes:
) The value returned in CX is a real mode segment address, not a protected
mode selector.
2.13 - Function 0201h - Set Real Mode Interrupt Vector:
-------------------------------------------------------
Sets the real mode segment:offset for the specified interrupt vector.
In:
AX = 0201h
BL = interrupt number
CX:DX = segment:offset of real mode interrupt handler
Notes:
) The value passed in CX must be a real mode segment address, not a protected
mode selector. Consequently, the interrupt handler must either reside in
DOS memory (below the 1M boundary) or the client must allocate a real mode
callback address.
2.14 - Function 0204h - Get Protected Mode Interrupt Vector:
------------------------------------------------------------
Returns the address of the current protected mode interrupt handler for the
specified interrupt.
In:
AX = 0204h
BL = interrupt number
Out:
always successful:
CX:EDX = selector:offset of protected mode interrupt handler
Notes:
) The value returned in CX is a valid protected mode selector, not a real mode
segment address.
2.15 - Function 0205h - Set Protected Mode Interrupt Vector:
------------------------------------------------------------
Sets the address of the protected mode interrupt handler for the specified
interrupt.
In:
AX = 0205h
BL = interrupt number
CX:EDX = selector offset of protected mode interrupt handler
Out:
if failed:
AX = error code:
8022h - invalid selector
Notes:
) The value passed in CX must be a valid protected mode selector, not a real
mode segment address.
2.16 - Function 0300h - Simulate Real Mode Interrupt:
-----------------------------------------------------
Simulates an interrupt in real mode. The function transfers control to the
address specified by the real mode interrupt vector. The real mode handler
must return by executing an IRET.
In:
AX = 0300h
BL = interrupt number
BH = must be 0
CX = number of words to copy from the protected mode stack to the real
mode stack
ES:EDI = selector:offset of real mode register data structure in the
following format:
Offset Length Contents
00h 4 EDI
04h 4 ESI
08h 4 EBP
0ch 4 reserved, ignored
10h 4 EBX
14h 4 EDX
18h 4 ECX
1ch 4 EAX
20h 2 CPU status flags
22h 2 ES
24h 2 DS
26h 2 FS
28h 2 GS
2ah 2 IP (reserved, ignored)
2ch 2 CS (reserved, ignored)
2eh 2 SP
30h 2 SS
Out:
if successful:
ES:EDI = selector offset of modified real mode register data structure
if failed:
AX = error code:
8012h - linear memory unavailable (stack)
8013h - physical memory unavailable (stack) (DPMI 1.0 only)
8014h - backing store unavailable (stack) (DPMI 1.0 only)
8021h - invalid value (CX too large) (DPMI 1.0 only)
Notes:
) The CS:IP in the real mode register data structure is ignored by this
function. The appropriate interrupt handler will be called based on the
value passed in BL.
) If the SS:SP fields in the real mode register data structure are zero, a
real mode stack will be provided by the host. Otherwise the real mode SS:SP
will be set to the specified values before the interrupt handler is called.
) The flags specified in the real mode register data structure will be put on
the real mode interrupt handler's IRET frame. The interrupt handler will be
called with the interrupt and trace flags clear.
) Values placed in the segment register positions of the data structure must
be valid for real mode. That is, the values must be paragraph addresses, not
protected mode selectors.
) The target real mode handler must return with the stack in the same state
as when it was called. This means that the real mode code may switch stacks
while it is running, but must return on the same stack that it was called
on and must return with an IRET.
) When this function returns, the real mode register data structure will
contain the values that were returned by the real mode interrupt handler.
The CS:IP and SS:SP values will be unmodified in the data structure.
) It is the caller's responsibility to remove any parameters that were pushed
on the protected mode stack.
2.17 - Function 0301h - Call Real Mode Procedure With Far Return Frame:
-----------------------------------------------------------------------
Simulates a FAR CALL to a real mode procedure. The called procedure must
return by executing a RETF instruction.
In:
AX = 0301h
BH = must be 0
CX = number of words to copy from the protected mode stack to the real
mode stack
ES:EDI = selector:offset of real mode register data structure in the
following format:
Offset Length Contents
00h 4 EDI
04h 4 ESI
08h 4 EBP
0ch 4 reserved, ignored
10h 4 EBX
14h 4 EDX
18h 4 ECX
1ch 4 EAX
20h 2 CPU status flags
22h 2 ES
24h 2 DS
26h 2 FS
28h 2 GS
2ah 2 IP
2ch 2 CS
2eh 2 SP
30h 2 SS
Out:
if successful:
ES:EDI = selector offset of modified real mode register data structure
if failed:
AX = error code:
8012h - linear memory unavailable (stack)
8013h - physical memory unavailable (stack) (DPMI 1.0 only)
8014h - backing store unavailable (stack) (DPMI 1.0 only)
8021h - invalid value (CX too large) (DPMI 1.0 only)
Notes:
) The CS:IP in the real mode register data structure specifies the address of
the real mode procedure to call.
) If the SS:SP fields in the real mode register data structure are zero, a
real mode stack will be provided by the host. Otherwise the real mode SS:SP
will be set to the specified values before the procedure is called.
) Values placed in the segment register positions of the data structure must
be valid for real mode. That is, the values must be paragraph addresses, not
protected mode selectors.
) The target real mode procedure must return with the stack in the same state
as when it was called. This means that the real mode code may switch stacks
while it is running, but must return on the same stack that it was called
on and must return with a RETF and should not clear the stack of any
parameters that were passed to it on the stack.
) When this function returns, the real mode register data structure will
contain the values that were returned by the real mode procedure. The CS:IP
and SS:SP values will be unmodified in the data structure.
) It is the caller's responsibility to remove any parameters that were pushed
on the protected mode stack.
2.18 - Function 0302h - Call Real Mode Procedure With IRET Frame:
-----------------------------------------------------------------
Simulates a FAR CALL with flags pushed on the stack to a real mode procedure.
The real mode procedure must return by executing an IRET instruction or a
RETF 2.
In:
AX = 0301h
BH = must be 0
CX = number of words to copy from the protected mode stack to the real
mode stack
ES:EDI = selector:offset of real mode register data structure in the
following format:
Offset Length Contents
00h 4 EDI
04h 4 ESI
08h 4 EBP
0ch 4 reserved, ignored
10h 4 EBX
14h 4 EDX
18h 4 ECX
1ch 4 EAX
20h 2 CPU status flags
22h 2 ES
24h 2 DS
26h 2 FS
28h 2 GS
2ah 2 IP
2ch 2 CS
2eh 2 SP
30h 2 SS
Out:
if successful:
ES:EDI = selector offset of modified real mode register data structure
if failed:
AX = error code:
8012h - linear memory unavailable (stack)
8013h - physical memory unavailable (stack) (DPMI 1.0 only)
8014h - backing store unavailable (stack) (DPMI 1.0 only)
8021h - invalid value (CX too large) (DPMI 1.0 only)
Notes:
) The CS:IP in the real mode register data structure specifies the address of
the real mode procedure to call.
) If the SS:SP fields in the real mode register data structure are zero, a
real mode stack will be provided by the host. Otherwise the real mode SS:SP
will be set to the specified values before the procedure is called.
) The flags specified in the real mode register data structure will be put on
the real mode procedure's IRET frame. The procedure will be called with the
interrupt and trace flags clear.
) Values placed in the segment register positions of the data structure must
be valid for real mode. That is, the values must be paragraph addresses, not
protected mode selectors.
) The target real mode procedure must return with the stack in the same state
as when it was called. This means that the real mode code may switch stacks
while it is running, but must return on the same stack that it was called
on and must return with an IRET or discard the flags from the stack with a
RETF 2 and should not clear the stack of any parameters that were passed to
it on the stack.
) When this function returns, the real mode register data structure will
contain the values that were returned by the real mode procedure. The CS:IP
and SS:SP values will be unmodified in the data structure.
) It is the caller's responsibility to remove any parameters that were pushed
on the protected mode stack.
2.19 - Function 0303h - Allocate Real Mode Callback Address:
------------------------------------------------------------
Returns a unique real mode segment:offset, known as a "real mode callback",
that will transfer control from real mode to a protected mode procedure.
Callback addresses obtained with this function can be passed by a protected
mode program to a real mode application, interrupt handler, device driver,
TSR, etc... so that the real mode program can call procedures within the
protected mode program.
In:
AX = 0303h
DS:ESI = selector:offset of protected mode procedure to call
ES:EDI = selector:offset of 32h byte buffer for real mode register data
structure to be used when calling the callback routine.
Out:
if successful:
CX:DX = segment:offset of real mode callback
if failed:
AX = error code:
8015h - callback unavailable
Notes:
) A descriptor may be allocated for each callback to hold the real mode SS
descriptor. Real mode callbacks are a limited system resource. A client
should release a callback that it is no longer using.
2.20 - Function 0304h - Free Real Mode Callback Address:
--------------------------------------------------------
Releases a real mode callback address that was previously allocated with the
Allocate Real Mode Callback Address function (0303h).
In:
AX = 0304h
CX:DX = segment:offset of real mode callback to be freed
Out:
if failed:
AX = error code:
8024h - invalid callback address
Notes:
) Real mode callbacks are a limited system resource. A client should release
any callback that it is no longer using.
2.21 - Function 0305h - Get State Save/Restore Addresses:
---------------------------------------------------------
Returns the address of two procedures used to save and restore the state of
the current task's registers in the mode (protected or real) which is not
currently executing.
In:
AX = 0305h
Out:
always successful:
AX = size of buffer in bytes required to save state
BX:CX = segment:offset of real mode routine used to save/restore state
SI:EDI = selector:offset of protected mode routine used to save/restore
state
Notes:
) The real mode segment:offset returned by this function should be called
only in real mode to save/restore the state of the protected mode registers.
The protected mode selector:offset returned by this function should be
called only in protected mode to save/restore the state of the real mode
registers.
) Both of the state save/restore procedures are entered by a FAR CALL with the
following parameters:
AL = 0 to save state
= 1 to restore state
ES:(E)DI = (selector or segment):offset of state buffer
The state buffer must be at least as large as the value returned in AX by
INT 31h function 0305h. The state save/restore procedures do not modify any
registers. DI must be used for the buffer offset in real mode, EDI must be
used in protected mode.
) Some DPMI hosts and VCPI/XMS/raw will not require the state to be saved,
indicating this by returning a buffer size of zero in AX. In such cases,
that addresses returned by this function can still be called, although they
will simply return without performing any useful function.
) Clients do not need to call the state save/restore procedures before using
INT 31h function 0300h, 0301h, or 0302h. The state save/restore procedures
are provided for clients that use the raw mode switch services only.
2.22 - Function 0306h - Get Raw Mode Switch Addresses:
------------------------------------------------------
Returns addresses that can be called for low level mode switching.
In:
AX = 0306h
Out:
always successful:
BX:CX = segment:offset of real to protected mode switch procedure
SI:EDI = selector:offset of protected to real mode switch procedure
Notes:
) The real mode segment:offset returned by this function should be called
only in real mode to switch to protected mode. The protected mode
selector:offset returned by this function should be called only in protected
mode to switch to real mode.
) The mode switch procedures are entered by a FAR JMP to the appropriate
address with the following parameters:
AX = new DS
CX = new ES
DX = new SS
(E)BX = new (E)SP
SI = new CS
(E)DI = new (E)IP
The processor is placed into the desired mode, and the DS, ES, SS, (E)SP,
CS, and (E)IP registers are updated with the specific values. In other
words, execution of the client continues in the requested mode at the
address provided in registers SI:(E)DI. The values specified to be placed
into the segment registers must be appropriate for the destination mode.
That is, segment addresses for real mode, and selectors for protected mode.
The values in EAX, EBX, ECX, EDX, ESI, and EDI after the mode switch are
undefined. EBP will be preserved across the mode switch call so it can be
used as a pointer. FS and GS will contain zero after the mode switch.
If interrupts are disabled when the mode switch procedure is invoked, they
will not be re-enabled by the host (even temporarily).
) It is up to the client to save and restore the state of the task when using
this function to switch modes. This requires the state save/restore
procedures whose addresses can be obtained with INT 31h function 0305h.
2.23 - Function 0400h - Get Version:
------------------------------------
Returns the version of the DPMI Specification implemented by the DPMI host.
The client can use this information to determine what functions are available.
In:
AX = 0400h
Out:
always successful:
AH = DPMI major version as a binary number (VCPI/XMS/raw returns 1)
AL = DPMI minor version as a binary number (VCPI/XMS/raw returns 0)
BX = flags:
Bits Significance
0 0 = host is 16bit (PMODE never runs under one of these)
1 = host is 32bit
1 0 = CPU returned to V86 mode for reflected interrupts
1 = CPU returned to real mode for reflected interrupts
2 0 = virtual memory not supported
1 = virtual memory supported
3-15 reserved
CL = processor type:
03h = 80386
04h = 80486
05h = 80586
06h-ffh = reserved
DH = current value of master PIC base interrupt (low 8 IRQs)
DL = current value of slave PIC base interrupt (high 8 IRQs)
Notes:
) The major and minor version numbers are binary, not BCD. So a DPMI 0.9
implementation would return AH as 0 and AL as 5ah (90).
2.24 - Function 0500h - Get Free Memory Information:
----------------------------------------------------
Returns information about the amount of available memory. Since DPMI clients
could be running in a multitasking environment, the information returned by
this function should be considered advisory.
In:
AX = 0500h
ES:EDI = selector:offset of 48 byte buffer
Out:
if successful:
buffer is filled with the following information:
if failed:
AX = error code:
8010h - internal resource unavailable (stack) (XMS only)
Offset Length Contents
00h 4 Largest available free block in bytes
04h 2ch Other fields only supplied by DPMI
Notes:
) Only the first field of the structure is guaranteed to contain a valid
value. Any fields that are not supported by the host will be set to -1
(0ffffffffh) to indicate that the information is not available.
2.25 - Function 0501h - Allocate Memory Block:
----------------------------------------------
Allocates a block of extended memory.
In:
AX = 0501h
BX:CX = size of block in bytes (must be non-zero)
Out:
if successful:
BX:CX = linear address of allocated memory block
SI:DI = memory block handle (used to resize and free block)
if failed:
AX = error code:
8010h - internal resource unavailable (stack) (XMS only)
8012h - linear memory unavailable (DPMI 1.0/VCPI only)
8013h - physical memory unavailable
8014h - backing store unavailable (DPMI 1.0 only)
8016h - handle unavailable (DPMI 1.0/XMS only)
8021h - invalid value (BX:CX = 0)
Notes:
) The allocated block is guaranteed to have at least paragraph alignment.
) This function does not allocate any descriptors for the memory block. It is
the responsibility of the client to allocate and initialize any descriptors
needed to access the memory with additional function calls.
) The allocations by this function could be paragraph, kilobyte, or page
aligned. That is, the value you request could be rounded up to the next
paragraph, kilobyte, or page value.
2.26 - Function 0502h - Free Memory Block:
------------------------------------------
Frees a memory block previously allocated with the Allocate Memory Block
function (0501h).
In:
AX = 0502h
SI:DI = memory block handle
Out:
if failed:
AX = error code:
8010h - internal resource unavailable (stack) (XMS only)
8023h - invalid handle
Notes:
) No descriptors are freed by this call. It is the client's responsibility to
free any descriptors that it previously allocated to map the memory block.
Descriptors should be freed before memory blocks.
2.27 - Function 0503h - Resize Memory Block:
--------------------------------------------
Changes the size of a memory block previously allocated with the Allocate
Memory Block function (0501h).
In:
AX = 0503h
BX:CX = new size of block in bytes (must be non-zero)
SI:DI = memory block handle
Out:
BX:CX = new linear address of memory block
SI:DI = new memory block handle
if failed:
AX = error code:
8010h - internal resource unavailable (stack) (XMS only)
8012h - linear memory unavailable (DPMI 1.0/VCPI only)
8013h - physical memory unavailable
8014h - backing store unavailable (DPMI 1.0 only)
8016h - handle unavailable (DPMI 1.0/XMS only)
8021h - invalid value (BX:CX = 0)
8023h - invalid handle
Notes:
) After this function returns successfully, the previous handle for the memory
block is invalid and should not be used anymore.
) It is the client's responsibility to update any descriptors that map the
memory block with the new linear address after resizing the block.
2.28 - Function 050Ah - Get Memory Block Size and Base:
-------------------------------------------------------
Returns the size and base of a memory block that was previously allocated
with the Allocate Memory Block function (0501h).
In:
AX = 050ah
SI:DI = memory block handle
Out:
if successful:
BX:CX = linear address of memory block
SI:DI = size of memory block (bytes)
if failed:
AX = error code:
8010h - internal resource unavailable (stack) (XMS only)
8023h - invalid handle
Notes:
) This function is not present under DPMI 0.9.
2.29 - Function 0900h - Get and Disable Virtual Interrupt State:
----------------------------------------------------------------
Disables the virtual interrupt flag and returns the previous state of it.
In:
AX = 0900h
Out:
always successful:
AL = 0 if virtual interrupts were previously disabled
AL = 1 if virtual interrupts were previously enabled
Notes:
) AH is not changed by this function. Therefore the previous state can be
restored by simply executing another INT 31h.
) A client that does not need to know the prior interrupt state can execute
the CLI instruction rather than calling this function. The instruction may
be trapped by a DPMI host and should be assumed to be very slow.
2.30 - Function 0901h - Get and Enable Virtual Interrupt State:
---------------------------------------------------------------
Enables the virtual interrupt flag and returns the previous state of it.
In:
AX = 0901h
Out:
always successful:
AL = 0 if virtual interrupts were previously disabled
AL = 1 if virtual interrupts were previously enabled
Notes:
) AH is not changed by this function. Therefore the previous state can be
retstored by simply executing another INT 31h.
) A client that does not need to know the prior interrupt state can execute
the STI instruction rather than calling this function. The instruction may
be trapped by a DPMI host and should be assumed to be very slow.
2.31 - Function 0902h - Get Virtual Interrupt State:
----------------------------------------------------
Returns the current state of the virtual interrupt flag.
In:
AX = 0902h
Out:
always successful:
AL = 0 if virtual interrupts are disabled
AL = 1 if virtual interrupts are enabled
Notes:
) This function should be used in preference to the PUSHF instruction to
examine the interrupt flag, because the PUSHF instruction returns the
physical interrupt flag rather than the virtualized interrupt flag. On some
DPMI hosts, the physical interrupt flag will always be enabled, even when
the hardware interrupts are not being passed through to the client.
2.32 - Function FFFFh - Special Fluffy Magical Function:
--------------------------------------------------------
Does special fluffy magical things.
In:
AX = ffffh
Out:
if successful:
AX = 1234h (normal fluffy magical number)
BX = 56ijh (special fluffy magical number)
CX = 89abh (nothing really special)
DX = cdefh (again, just a boring number)
EX = return value from beyond
FX = an acronym for 'effects'
GX = huh?
HX = size of special fluffy magical buffer
if failed:
AX = error code:
8001h - get a grip on reality!
Notes:
) The special fluffy magical buffer must not exceed the special fluffy magical
constant in size, which is defined in the special fluffy magical place.
) Well, maybe it exists... Maybe if you try calling it many many many many
many many many many times in a row it will succeed.
------------------------------------------------------------------------------
3 - Miscellenaous:
------------------
Some final things about PMODE, including some low level tech info if you are
just curious.
3.0 - Glossary:
---------------
Bottom up allocation - A method of extended memory allocation which relies on
control blocks specifying the start of free extended memory. This is the
VDISK extended memory allocation scheme.
Client - A program which uses DPMI INT 2fh and INT 31h services to run in
protected mode.
CPL, Current Privilege Level - The privilege level of the currently executing
code.
Descriptor - An 8 byte structure which defines a segment type, its base
address and limit, and the type of access allowed to it.
DPL, Descriptor Privilege Level - The privilege level of a descriptor.
Descriptor protection is based on certain rules of the CPL and DPL of a
descriptor that code may want to access.
DPMI, DOS Protected Mode Interface - An interface for protected mode DOS
programs to manage memory, interrupts, exceptions, debugging registers, and
coprocessor emulation in a well behaved manner which allows them to coexist
with other protected mode programs and operating systems.
Exception - An interrupt that occurs because of some violation of protection
rules.
Extended memory - Memory which lies above the 1M boundary and can only be
addressed in protected mode.
GDT, Global Descriptor Table - A descriptor table which can contain many types
of descriptors besides the regular code and data descriptors. This can
include TSS descriptors and LDT descriptors.
Host - A program which provides DPMI protected mode services.
IDT, Interrupt Descriptor Table - A descriptor table which contains the
gate descriptors to the handlers for the 256 interrupts.
LDT, Local Descriptor Table - A descriptor table which usually contains all
the descriptors of a particular task. Each task in a 386 multitasking system
can have its own LDT whereas there is only one GDT for the entire system.
Linear memory - Address space rather than actual physical RAM of ROM.
Page - A 4k chunk of memory. The 80386 can map any 4k chunk of physical memory
to any linear address. There are also some protection rules that can apply
to pages, such as read-only, or privilege level checking on access.
Page Directory - Sort of a master page table which maps page tables instead of
pages.
Page Table - A 4k table containing 1024 entries for pages. Each page table
maps 4 megabytes of linear memory to physical memory.
Physical memory - The actual physical memory present in a system.
Privilege level - A numeric value representing the freedom of system access
and how much protection applies to code. This value ranges from
0 (most free (godlike)) to 3 (least free (lowly slave)).
RPL, Requestor Privilege Level - The privilege level code requests for a
specific selector access. It is contained in the low two bits of the
selector.
Segment - A specific linear chunk of memory. In real mode, segments are
limited to the first megabyte of memory and are always 64k in length. In
protected mode, a segment can start anywhere in the entire 4 gigabyte
address space of the 80386 and can be that long.
Selector - An index in protected mode into a descriptor table. Selectors are
used in place of segments in protected mode in the segment registers.
Top down allocation - A method of extended memory management which relies on
the BIOS INT 15h function 88h. A program which needs to allocate extended
memory will hook INT 15h and return a smaller extended memory size. The
program is then free to use the memory between the previous top of extended
memory and what it returns as the top of extended memory without worrying
about other programs overwriting its extended memory.
TSS, Task State Segment - A special memory structure used in task switching
and protection.
V86 mode - Actually it is protected mode, running at a privilege level of 3.
Segment registers are used in the same manner as in real mode, with segment
addresses rather than selectors. The advantage is that paging and other
protected tasks can be active, which includes other V86 mode tasks. The
disadvantage is that it is slower than real mode.
VCPI, Virtual Control Program Interface - The predecessor to DPMI. VCPI
extends the EMS interface to allow DOS programs to run in protected mode
in the presence of EMS emulators or other 80386 control programs.
Virtual memory - Extra memory beyond the actual physical memory present in a
system. There is not really any more physical memory in the system, but an
operating system or DPMI host can give that illusion by swapping the
contents of physical memory to and from a disk and mapping that physical
memory to different linear addresses.
XMS, eXtended Memory Specification - A handle based extended memory management
interface.
3.1 - Differences between modes:
--------------------------------
Some differences between DPMI, VCPI, XMS, and raw protected mode:
) DPMI:
Client descriptors reside in a LDT.
VCPI/XMS/raw:
Client descriptors reside in the GDT.
) DPMI:
Code runs at CPL 3. (I don't know of any DPMI that doesn't)
VCPI/XMS/raw:
Code runs at CPL 0. (Much faster)
) DPMI/VCPI:
Real mode calls are executed in V86 mode.
) XMS/raw:
Real mode calls are executed in real mode. (Much faster)
) DPMI/VCPI:
Paging is enabled.
XMS/raw:
Paging is disabled. (Not really very much faster, but what the hell)
) DPMI:
IRET(D) and POPF(D) may not affect the interrupt flag. PUSHF(D) may not
store the interrupt flag.
VCPI/XMS/raw:
IRET(D) and POPF(D) affect the real interrupt flag. PUSHF(D) stores the
real interrupt flag.
) DPMI:
INTs 1Ch, 23h, and 24h from real mode are sent to protected mode first.
This means if a protected mode handler is installed for these interrupts,
it will get control.
) VCPI/XMS/raw:
DPMI dox are not too clear on the method these interrupts are called in.
Callbacks I would guess. But I am too lazy to investigate. And frankly, I
don't give a shit. So the VCPI/XMS/raw kernel does not support this.
) DPMI:
Seperates exceptions from other low interrupts and IRQs. That is, an
exception 8 would never erroneously go to the handler for IRQ 0.
) VCPI/XMS/raw:
Hey, exceptions are bad, you should not be getting them in the first
place. Reprogramming the interrupt controllers or running the client code
at a lower privilege is not worth the slowdown for me.
) DPMI:
Theoretically, DPMI may refuse a request to set a segment limit to 4G.
I have not yet found a DPMI that will refuse this. They all do protection
at the paging level. And a high limit is necessary for flat mode and
negative offsets.
) VCPI/XMS/raw:
There is no protection, no segment limit or base address will ever be
denied.
) DPMI:
Memory allocation is almost always page granular, but not necessarily.
VCPI:
Memory allocation is page granular (4k chunks).
XMS:
Memory allocation is kilobyte granular.
raw:
Memory allocation is paragraph granular.
) DPMI/VCPI:
Low memory linear addresses may or may not be the actual physical
addresses. Extended memory addresses are almost sure not to be.
XMS/raw:
All linear addresses are physical addresses.
3.2 - Notes:
------------
Here are some misc and low level technical details about PMODE and some
points I want to emphasize. Some of them may seem very obscure, with no real
need to list. But for the sake of thorough documentation, they are here:
) In protected mode, ESP must always be the stack pointer. Meaning, even if
using a 16bit stack segment, the high word of ESP MUST be 0.
) When calling the raw mode switching routine to switch into protected mode,
you must supply the full ESP and EIP. Even if the stack or code segments
being switched to are 16bit.
) If the call to init protected mode was from a segment other than PMODE_TEXT
under DPMI, and a descriptor can not be allocated for that code segment,
immediate termination results. But this should never happen. A DPMI host
that can not supply even one descriptor to its protected mode clients defies
logic. But for the sake of covering all possible screw-ups, this condition
is checked for.
) DPMI 1.0/VCPI/XMS/raw will reload any segment registers for which the
descriptor is changed through an INT 31 function. DPMI 0.9 does not.
(Actually it does, through pushing and then popping any segment registers
it uses. But it is not guaranteed to reload any or all of them).
) DPMI 1.0/VCPI/XMS/raw will zero any segment registers freed with INT 31
function 0001h. DPMI 0.9 may or may not.
) I dont know about DPMI with respect to reloading or freeing a selector which
is currently loaded into SS, but the VCPI/XMS/raw system will not reload or
zero SS. You should not be modifying your stack or code descriptor as you
are using it. This could be bad, even if the INT 31 were handled through a
task gate (which would be slow).
) Remember that DPMI 0.9 does not return error codes as DPMI 1.0/VCPI/XMS/raw
do.
) Reasons for calls to real mode executing in actual real mode rather than
V86 mode (XMS/raw):
) V86 call system can not be used under VCPI very successfully. (Yes, VCPI
runs its real mode in V86. But VCPI MUST be in control in this case.)
) Real mode runs faster than V86 mode.
) INT 15h and XMS extended memory functions will work.
) Other protected mode programs that run DOS functions in real mode will
work.
) It is faster to switch between protected/real mode than protected/V86.
) Use LAR to find out the current CPL for setting descriptor access right.
) You must always set the present bit when setting descriptor access rights.
) Under the VCPI/XMS/raw system, the AVL bit of descriptors is used to keep
track of free and used descriptors. The value you pass for this bit when
setting descriptor access rights will be ignored.
) When switching modes using the raw switching routines, make sure there is
some space on both stacks (real and protected). Specific DPMI requirements
may vary, but 64 bytes is enough for VCPI/XMS/raw.
) In protected mode, remember to use IRETD, not IRET. When DPMI documentation
refers to using IRET, it is actually referring to the 32bit version of the
instruction under 32bit systems, which is IRETD.
) Remember that free memory information is just advisory. A TSR or another
task in a multitasking system might grab some memory in between a call to
INT 31 function 0500h and INT 31 function 0501h, even if you disable
interrupts.
) The _pm_? variables are not checked for validity. So don't set them outside
reasonable bounds. For example, don't ask for 20h real mode stacks of size
1000h paragraphs. This is two megabytes of real mode stack space. This is
way too much, considering that all real mode available low memory
encompasses 640k.
) You should allocate memory in large blocks. Memory space is subject to
fragmentation. Although you can help the situation a little under VCPI by
setting a high number of page tables. This will not increase the physical
memory available, but it will increase the address space available to put
that memory into linear chunks.
) I tried to balance clean, well designed code, with size and speed. True,
some things are not as absolutely optimal as they can be. But the source is
right here. Very clean and commented. If you truly need those last ounces of
speed, feel free to modify it (this does not free you from the obligation of
crediting me for it).
) I can not control what DPMI does. But under XMS/raw, code in protected mode
runs at the fastest speed possible. That is, there is no privilege checking
to get in the way. No exception will rip control away from sensitive
instructions. Not even paging, with its memory references every time a page
table entry is not found in the TLB. Under VCPI, all of this applies except
the paging. Which really isn't that bad.
) Under VCPI, free memory information is validated in function 0500h by
actually allocating that memory and releasing it before passing the
information to your program. This because under some multitasking systems,
the VCPI function for getting the memory available may return information
for the whole system. While the multitasking system may impose allocation
limits on the specific task your code is part of.
) Under VCPI/XMS/raw, INT 31 function 0500h will return only the first field
of the buffer set. All the other fields will be set to 0ffffffffh.
) Under VCPI/XMS/raw, an allocate descriptor INT 31 function 0000h called with
CX = 0 will return error 8021h. DPMI dox dont state this, and I dont know if
DPMI returns an error on CX = 0.
) INT 31 functions 0300h, 0301h, and 0302h will always inform you if there is
not enough real mode stack space. But and IRQ or INT redirection can not.
In this case, the PC speaker will be turned on, and the machine will be
hung. This is better than allowing it to overrun data below it with
unpredictable results. And hey, I dont need no stinkin debug code cluttering
up my nice and pretty extender. I just need it to tell me in case something
like this happenes. If you want debug code, go and hack it in yourself.
) DPMI dox dont state this, but the alias descriptor INT 31 function 000ah
creates is always an expand-up and writeable data descriptor. No matter what
type the source descriptor is.
) You should limit yourself to allocating as few individual memory blocks as
possible. Under XMS, there is usually a strict limit on how many blocks can
be allocated (normally 32).
) DPMI dox state that the field between EBP and EBX should be zero upon an
INT 31 function 0300h, but is ignored by functions 0301h and 0302h. That is
just a stupid typo, the field will be ignored by function 0300h.
) Under XMS an extra 15 bytes will be allocated for possible aligning of the
XMS memory block on a paragraph. Though an XMS block will probably already
be aligned on at least a paragraph boundary, this is not defined in the XMS
standard. And to keep the possibility of problems at nil, this is done.
) Be aware that memory allocation functions under XMS use real mode calls and
real mode stack space defined with _pm_rmstacklen and _pm_rmstacks. If there
is not enough stack space for the call to the real mode XMS driver, error
code 8010h (resource unavailable) will be returned.
) If an XMS memory lock fails, which is used in memory allocation functions,
error 8010h will be returned. A memory lock failure is not due to memory not
being available. But rather, some internal XMS crap. But it should never
happen anyway.
) The raw system checks both INT 15h and the VDISK low to high extended memory
allocation scheme to get its available extended memory area.
) The raw system allocates extended memory on an as-needed basis from the top
down. INT 15h function 88h is hooked and the total amount of memory
allocated using the kernel function 0501h is subtracted from the amount of
memory returned from the previous INT 15h handler. This is so that you can
execute other protected mode programs from within your programs and they
will have extended memory available (if you left any).
) A protected mode IRQ handler or real mode callback must return on the same
stack it was called with.
) A real mode routine called with functions 0300h, 0301h, or 0302h must return
on the same stack it was called with.
) You should make no assumptions about the low memory protected mode data area
needed by PMODE. It can range from very small to very large. And if a DPMI
host is present, it is unpredictable.
) Make sure you do not access, read or write, extended memory outside the
blocks you allocate. Even if there is no physical memory there, you will
probably get exceptions under DPMI/VCPI.
) When setting descriptor access rights, remember that the B bit of stack
descriptors determines whether PUSHes and POPs use SP (B=0) or ESP (B=1).
) If you enable interrupts in a callback, you MUST assume DS is no longer
valid. Even if you are sure your callback will not be re-entered. This is
because PMODE uses the same DS selector for ALL real mode callbacks.
) The reserved field between EBP and EBX in the register structure used during
a callback is used by PMODE to preserve the high word of ESP for real mode.
------------------------------------------------------------------------------
3.3 - Final word:
-----------------
I like this latest PMODE a lot. For an extender, it is clean, solid, fast,
and small. It is not limited to assembly code. It does not need control at
startup, but may be initialized at any time. It can be turned into basically
any type of extender. It can work with high level languages. You will probably
also like it because it is free, and the source code is provided. The DPMI
interface assures portability and long life. Enjoy protected mode, and
remember the credits.
If you really really really really must contact me, try tran@phantom.com.
I do not guarantee a response, so don't get pissed off if you don't get one.
L8r...
Tran...